package com.m3839.sdk.ym.core

import android.app.Activity
import android.app.Application.ActivityLifecycleCallbacks
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.text.TextUtils
import android.view.Gravity
import android.widget.ImageView
import android.widget.LinearLayout
import com.m3839.sdk.ym.YouthModelConfig
import com.m3839.sdk.ym.constant.YMCodeConstant
import com.m3839.sdk.ym.constant.YMParamConstant
import com.m3839.sdk.ym.constant.YMStringConstant
import com.m3839.sdk.ym.ext.*
import com.m3839.sdk.ym.ui.YMModeDialogFragment
import com.m3839.sdk.ym.ui.YMPasswordDialogFragment
import com.m3839.sdk.ym.util.AppUtils
import com.m3839.sdk.ym.util.HandlerUtils
import com.m3839.sdk.ym.util.YMLogUtils
import com.m3839.sdk.ym.widget.toast.YMFloatView
import com.m3839.sdk.ym.widget.toast.OnCancelListener
import com.m3839.sdk.ym.widget.toast.OnClickListener

/**
 * @description: YMManager
 *
 * @author ChenBaoyang
 * @date 5/27/21 10:26
 */
class YMManager private constructor() : OnHeartListener {


    /**
     * 初始化事件的回调函数
     */
    private var callback: ((code: Int, message: String) -> Unit)? = null

    /**
     * 游戏主Activity对象
     */
    private lateinit var activity: Activity

    /**
     * 内部配置信息
     */
    private lateinit var internalConfig: YMInternalConfig

    /**
     * 表示是否从后台切换回来
     */
    private var isFromBackGround: Boolean = false

    /**
     * 心跳控制器
     */
    private lateinit var controller: IYMHeartController

    /**
     * 倒计时提示悬浮控件
     */
    private var ymFcmFloatView: YMFloatView? = null

    /**
     * 青少年的展示悬浮
     */
    private var ymTipFloatView: YMFloatView? = null

    /**
     * 用来判断是否是新一轮的防沉迷
     */
    private var isNewFcm = false

    /**
     * 倒计时的提醒点
     */
    private val moments: MutableList<Int> = mutableListOf()

    companion object {
        /** YMManager实例对象 */
        val instance = SingletonHolder.holder
        /** 心跳间隔时长默认值 */
        const val HEART_INTERVAL = 60

        private const val TAG = "YMManager"

        private const val HOUR_UNIT = 60 * 60.toLong()
        private const val MINUTE_UNIT: Long = 60

        private const val DELAY_TIME: Long = 500
    }

    private object SingletonHolder {
        val holder= YMManager()
    }

    init {
        moments.add(900) //15分钟
        moments.add(600) //10分钟
        moments.add(300) //5分钟
    }

    fun init(activity: Activity, config: YouthModelConfig, callback: ((code: Int, message: String) -> Unit)? = null) {
        this.activity = activity
        this.callback = callback
        //解析配置信息
        parseConfig(config)
        //初始化心跳控制器
        initHeartController()
        //注册全局的activity生命周期监听
        registerActivityLifecycleCallbacks()

        //存储默认密码配置
        val defaultPassword: String? = internalConfig.defaultPassword
        YMStorage.instance.saveDefaultPassword(activity, defaultPassword!!)
        //存储游戏方向配置
        val defaultOrientation: Int = internalConfig.orientation
        YMStorage.instance.saveDefaultOrientation(activity, defaultOrientation)

        //初始化时间的相关配置信息，用来做节假日和非节假日、夜间模式的管理
        YMDateManager.instance.initConfig(
            internalConfig.holidayList, internalConfig.workdayList, internalConfig.nightEndTime,
            internalConfig.nightStartTime
            )

        //判断一下游戏是否设置找回密码的联系方式，如果没有设置，回调给游戏方
        if (TextUtils.isEmpty(internalConfig.contact) && callback != null) {
            callback(
                YMCodeConstant.YM_CODE_CONTACT_EMPTY,
                YMCodeConstant.YM_MSG_CONTACT_EMPTY
            )
        }
        //检查是否还有游戏时长，没有的话，弹出继续时长的密码防沉迷页面
        checkFcmNotTime()
    }

    /**
     * 用来初始化的时候检测是否还有时长
     */
    fun checkFcmNotTime() {
        //判断是否处于防沉迷状态，且已经无累计时长，则需要弹出继续青少年模式界面
        val fcmNotTime: Boolean = getFcmNotTime()
        if (fcmNotTime) {
            val content = java.lang.String.format(
                YMStringConstant.YM_FCM_CONTINUE_FCM_TIP,
                getTodayPlayTime()
            )

            // 跳转到继续防沉迷页面
            YMPasswordDialogFragment.show(
                activity,
                YMParamConstant.YM_VALUE_ACTION_CONTINUE_FCM,
                YMStringConstant.YM_FCM_CONTINUE_FCM,
                content,
                YMStringConstant.YM_FCM_IMMEDIATE_VERIFY,
                "",
                YMStringConstant.YM_FRAGMENT_TAG_PWD_FCM_CONTINUE
            )
        } else {
            setShowFcmPop()
        }
    }


    /**
     * 解析青少年模式的配置信息
     *
     * @param config [YouthModelConfig]
     */
    private fun parseConfig(config: YouthModelConfig) {
        internalConfig = YMInternalConfig().apply {
            normalPlayTime = config.normalPlayTime
            holidayPlayTime = config.holidayPlayTime
            contact = config.contact
            defaultPassword = config.defaultPassword
            orientation = if(config.isLand) {ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE} else {ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}
            nightStartTime = config.nightStartTime.toString()
            nightEndTime = config.nightEndTime.toString()
            workdayList = config.workdayList
            holidayList = config.holidayList
        }
    }

    /**
     * 初始化心跳
     */
    private fun initHeartController() {
        controller = YMHeartController()
        controller.init(HEART_INTERVAL, this)

    }


    /**
     * 开启防沉迷进行心跳统计
     *
     * @param remainingTime 值为-1表示重新开始， 有值表示继续消耗剩余的时间
     */
    fun startFcm(remainingTime: Long) {
        //保存防沉迷的状态
        YMStorage.instance.saveFcmStatus(activity, true)
        if (remainingTime <= 0) {
            // 计算当天可游戏时长的时间，单位是秒
            val playTime = (getTodayPlayTime() * HOUR_UNIT).toLong()
            // 设置剩余可玩的时间到倒计时器中，单位是秒
            controller.remainingTime = playTime
            // 设置倒计时提示事件段
            controller.moments = moments
            // 获取当天日期
            val today: String = YMDateManager.instance.todayDate
            // 保存当天的可玩时间
            YMStorage.instance.saveFcmRemainTime(activity, today, playTime)
            // 如果是通过设置密码完，新设置的防沉迷，不马上显示倒计时
            if (!isNewFcm) {
                // 判断要显示的是小时还是分钟，大于一个小时，显示单位是小时
                if (playTime > HOUR_UNIT) {
                    showFcmFloat(YMStringConstant.getFloatTipHour(playTime))
                } else {
                    showFcmFloat(YMStringConstant.getFloatTipMinute(playTime))
                }
            }
        } else {
            // 设置剩余可玩的时间到倒计时器中，单位是秒
            controller.remainingTime = remainingTime
            controller.moments = moments
            // 判断要显示的是小时还是分钟，大于一个小时，显示单位是小时
            if (remainingTime > HOUR_UNIT) {
                showFcmFloat(YMStringConstant.getFloatTipHour(remainingTime))
            } else {
                showFcmFloat(YMStringConstant.getFloatTipMinute(remainingTime))
            }
        }

        //开始进行心跳计时
        controller.start()
        // 注册activity的声明周期监听
        registerActivityLifecycleCallbacks()
        // 回调给开发者告知开启防沉迷
        callback?.let { it(YMCodeConstant.YM_CODE_OPEN_FCM, YMCodeConstant.YM_MSG_OPEN_FCM) }
    }

    /**
     * 设置显示是否是新的一轮防沉迷
     * 使用的场景：
     *
     * 第一次设置密码开启新的防沉迷
     *
     * 继续青少年模式输入密码开启新的防沉迷
     */
    fun setNewFcm(isNewFcm: Boolean) {
        this.isNewFcm = isNewFcm
    }

    /**
     * 用来单独调用显示倒计时悬浮气泡
     * 用到的场景：
     * [YMModeDialogFragment]  第一次开启防沉迷的时候
     * [YMPasswordDialogFragment]  继续防沉迷的是时候
     */
    fun setShowFcmPop() {
        val remainingTime = getRemainingTime()
        // 判断要显示的是小时还是分钟，大于一个小时，显示单位是小时
        if (remainingTime > HOUR_UNIT) {
            showFcmFloat(YMStringConstant.getFloatTipHour(remainingTime))
        } else {
            showFcmFloat(YMStringConstant.getFloatTipMinute(remainingTime))
        }
        setNewFcm(false)
    }




    /**
     * 注册监听游戏的Activity的生命周期
     */
    private fun registerActivityLifecycleCallbacks() {
        activity.application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
    }

    /**
     * 注销监听游戏的Activity的生命周期
     */
    private fun unregisterActivityLifecycleCallbacks() {
        activity.application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
    }

    private val lifecycleCallbacks: ActivityLifecycleCallbacks =
        object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(
                activity: Activity,
                savedInstanceState: Bundle?
            ) {
                YMLogUtils.i(TAG, "onActivityCreated")
            }

            override fun onActivityStarted(activity: Activity) {
                YMLogUtils.i(TAG, "onActivityStarted")
                if (isFromBackGround) {
                    if (!getFcmNotTime()) {
                        controller.pause(false)
                    }
                    isFromBackGround = false
                }
            }

            override fun onActivityResumed(activity: Activity) {
                YMLogUtils.i(TAG, "onActivityResumed")
                instance.activity = activity
            }

            override fun onActivityPaused(activity: Activity) {
                YMLogUtils.i(TAG, "onActivityPaused")
            }

            override fun onActivityStopped(activity: Activity) {
                YMLogUtils.i(TAG, "onActivityStopped")
                if (AppUtils.isAppIsInBackground(activity)) {
                    isFromBackGround = true
                    controller.pause(true)
                }
            }

            override fun onActivitySaveInstanceState(
                activity: Activity,
                outState: Bundle
            ) {
                YMLogUtils.i(TAG, "onActivitySaveInstanceState")
            }

            override fun onActivityDestroyed(activity: Activity) {
                YMLogUtils.i(TAG, "onActivityDestroyed")
            }
        }

    /**
     * 心跳信息回调
     * @param time Long  表示剩余游戏时长
     */
    override fun onBeat(time: Long) {
        // 保存剩余时间
        val today: String = YMDateManager.instance.todayDate
        YMStorage.instance.saveFcmRemainTime(activity, today, time)
        val fcmStatus = getFcmStatus()
        if (time <= 0 && fcmStatus) {
            //隐藏掉悬浮球
            hideTipFloat()
            //保存当前已经累计时长
            YMStorage.instance.saveFcmNotTime(activity, today, true)
            //打开继续青少年模式的页面
            val content = java.lang.String.format(
                YMStringConstant.YM_FCM_CONTINUE_FCM_TIP,
                getTodayPlayTime()
            )
            // 跳转到继续防沉迷页面
            YMPasswordDialogFragment.show(
                activity,
                YMParamConstant.YM_VALUE_ACTION_CONTINUE_FCM,
                YMStringConstant.YM_FCM_CONTINUE_FCM,
                content,
                YMStringConstant.YM_FCM_IMMEDIATE_VERIFY,
                "",
                YMStringConstant.YM_FRAGMENT_TAG_PWD_FCM_CONTINUE
            )
        }
    }

    /**
     * 倒计时时间点回调
     * @param moment Int 剩余的时间点
     */
    override fun onCountDown(moment: Int) {
        //显示倒计时的悬浮气泡
        val minute = moment.div(MINUTE_UNIT)
        showFcmFloat("今日剩余游戏时长：${minute}分钟")

    }


    /**
     * 夜间模式的变化回调
     * @param isNightMode Boolean 表示是否在夜间模式
     */
    override fun onNightMode(isNightMode: Boolean) {
        if(isNightMode){
            YMLogUtils.i(TAG, "onNightMode ")
            HandlerUtils.runPostDelayed(Runnable {
                hideFcmFloat()
                hideTipFloat()
                //夜间模式
                YMModeDialogFragment.show(activity,
                    isOpenFcm = true,
                    isNightMode = true,
                    tag = YMStringConstant.YM_FRAGMENT_TAG_MODE_NIGHT
                )
            }, DELAY_TIME)
        }else{
            YMUIStackManager.instance?.finishAllFragment()
            showTipFloat()
        }
    }

    /**
     * 时间点，进入新的一天
     */
    override fun onNewDay() {
        //过了十二点重新开始算
        startFcm(-1)
    }

    /**
     * 进一步确定当前游戏或者应用是否在后台
     * @param isBack Boolean
     */
    override fun onBackground(isBack: Boolean) {
        isFromBackGround = isBack
    }

    /**
     * 显示剩余时长的悬浮气泡
     * 业务功能点：<h4> 当前气泡取消后，需要去主动展示“青少年模式”的气泡 [YMManager.showTipFloat] </h4>
     */
    private fun showFcmFloat(tip: String?) {
        //显示之前先隐藏青少年模式悬浮按钮
        val canPlay: Boolean = YMDateManager.instance.betweenPlayTime()
        if (!getFcmStatus() || !canPlay) {
            return
        }

        //hideTipFloat();
        if (ymFcmFloatView != null) {
            ymFcmFloatView!!.setText(activity.getId("tv_tip"), tip)
            ymFcmFloatView!!.setOnCancelListener(object : OnCancelListener {
                override fun onCancel() {
                    YMLogUtils.i("m3838", "自动关闭悬浮球")
                    ymFcmFloatView = null
                    showTipFloat()
                }
            })
            ymFcmFloatView!!.show()
            return
        }
        ymFcmFloatView = YMFloatView(activity)
            .setDuration(3000)
            .setOnCancelListener(object : OnCancelListener {
                override fun onCancel() {
                    YMLogUtils.i("m3838", "自动关闭悬浮球")
                    ymFcmFloatView = null
                    showTipFloat()
                }
            })
            .setView(activity.getLayoutId( "ym_float_count_down"))
            .setXOffset(0)
            .setYOffset(activity.dip2px(18f))
            .setGravity(Gravity.TOP or Gravity.START)
            .setText(activity.getId("tv_tip"), tip)
            .setOnClickListener(activity.getId("iv_close"),
                object : OnClickListener<ImageView> {
                    override fun onClick(floatView: YMFloatView, view: ImageView) {
                        floatView.cancel()
                        ymFcmFloatView = null
                        showTipFloat()
                    }
                })
            .show()
    }

    /**
     * 显示青少年模式悬浮按钮
     */
    fun showTipFloat() {
        val canPlay: Boolean = YMDateManager.instance.betweenPlayTime()
        if (!getFcmStatus() || !canPlay) {
            YMLogUtils.e("showTipFloat", "canPlay = " + canPlay + "; hadFcmOpen = " + getFcmStatus())
            return
        }
        if (ymTipFloatView != null && ymTipFloatView!!.isShow) {
            ymTipFloatView!!.update()
            YMLogUtils.e("showTipFloat", "++++++++++++update = ")
            return
        }
        if (ymTipFloatView != null) {
            ymTipFloatView!!.setText(
                activity.getId( "tv_title"),
                YMStringConstant.YM_FCM_MODE_TITLE
            )
            ymTipFloatView!!.show()
            YMLogUtils.e("showTipFloat", "++++++++++++show = ")
            return
        }
        ymTipFloatView = YMFloatView(activity)
            .setYOffset(-activity.getScreenHeight + activity.dip2px(18f))
            .setView(activity.getLayoutId("hykb_float_fcm"))
            .setText(
                activity.getId("tv_title"),
                YMStringConstant.YM_FCM_MODE_TITLE
            )
            .setOnClickListener(activity.getId("ll_fcm"),
                object : OnClickListener<LinearLayout> {
                    override fun onClick(floatView: YMFloatView, view: LinearLayout) {
                        // 进入到青少年模式已开启页面
                        YMModeDialogFragment.show(activity, isOpenFcm = true, tag = YMStringConstant.YM_FRAGMENT_TAG_MODE_OPEN)
                    }
                })
            .show()
    }

    /**
     * 隐藏青少年模式浮气泡
     */
    private fun hideTipFloat() {
        if (ymTipFloatView != null) {
            ymTipFloatView!!.cancel()
            ymTipFloatView = null
        }
    }

    /**
     * 隐藏青少年模式悬剩余时长气泡
     */
    private fun hideFcmFloat() {
        if (ymFcmFloatView != null) {
            ymFcmFloatView!!.cancel()
            ymFcmFloatView = null
        }
    }

    /**
     * 关闭防沉迷
     * 相关的停止操作：
     * [.handleCallBack] 回调给开发者
     * [YMStorage.saveFcmStatus] 更新防沉迷的状态
     * [YMStorage.saveFcmRemainTime] 更新缓存的本地时长
     * [YMHeartController.stop] 结束心跳
     */
    fun closeFcm() {
        //关闭的同时隐藏，悬浮球
        hideTipFloat()
        // 回调给开发者告知防沉迷已经关闭
        handleCallBack(YMCodeConstant.YM_CODE_CLOSE_FCM, YMCodeConstant.YM_MSG_CLOSE_FCM)
        // 更新防沉迷状态
        YMStorage.instance.saveFcmStatus(activity, false)
        // 清空掉剩余可玩时间
        val today: String = YMDateManager.instance.todayDate
        YMStorage.instance.saveFcmRemainTime(activity, today, 0L)
        // 停止心跳操作
        controller.stop()
        // 注销activity的声明周期监听
        unregisterActivityLifecycleCallbacks()
    }


    /**
     * 处理快爆回调给开发者
     * 返回的code和msg 来自[YMCodeConstant] 类
     *
     * @param code 返回给开发者当前流程的操作编码
     * @param msg  返回给开发者当前流程的操作内容提示
     */
    fun handleCallBack(code: Int, msg: String) {
        callback?.let { it(code, msg) }
    }

    /**
     * 获取当天可玩的时间
     *
     * 先通过 [YMDateManager.isHoliday] 方法判断是否是节假日
     *
     * @return 返回当天可玩的时长
     */
    fun getTodayPlayTime(): Float {
        val isHoliday: Boolean = YMDateManager.instance.isHoliday
        return if (isHoliday) internalConfig.holidayPlayTime else internalConfig.normalPlayTime
    }

    /**
     * 获取当天的夜间模式开始时间
     *
     * 通过 [YMDateManager.parseTime] 方法解析出具体的时间点
     */
    fun getNightStartTime(): Int {
        return YMDateManager.instance.parseTime(internalConfig.nightStartTime)
    }

    /**
     * 获取当天的夜间模式结束时间
     *
     * 通过 [YMDateManager.parseTime] 方法解析出具体的时间点
     */
    fun getNightEndTime(): Int {
        return YMDateManager.instance.parseTime(internalConfig.nightEndTime)
    }

    /**
     * 获取防沉迷的剩余时间
     *
     * 通过 [YMDateManager.todayDate] 方法读取到当天的日期，将日期作为参数key
     * 再通过 [YMStorage.getFcmRemainTime] 方法读取本地的缓存时长
     */
    fun getRemainingTime(): Long {
        val today: String = YMDateManager.instance.todayDate
        return YMStorage.instance.getFcmRemainTime(activity, today)
    }

    /**
     * 获取防沉迷的状态
     */
    fun getFcmStatus(): Boolean {
        return YMStorage.instance.getFcmStatus(activity, false)
    }

    /**
     * 保存防沉迷的当前无累计时长的状态
     *
     * 通过 [YMDateManager.todayDate] 方法读取到当天的日期，将日期作为参数key
     * 再通过 [YMStorage.getFcmNotTime] 方法读取当天防沉迷无时长的状态
     */
    fun getFcmNotTime(): Boolean {
        val today: String = YMDateManager.instance.todayDate
        return YMStorage.instance.getFcmNotTime(activity, today)
    }

    /**
     * 保存防沉迷的当前无累计时长的状态
     *
     * 再通过 [YMStorage.saveFcmNotTime] 方法保存当天防沉迷无时长的状态
     */
    fun saveFcmNotTime() {
        val today: String = YMDateManager.instance.todayDate
        YMStorage.instance.saveFcmNotTime(activity, today, false)
    }

    /**
     * 用来判断是否要用统一游戏时长
     *
     * @return
     */
    fun diffFcm(): Boolean {
        return getNormalPlayTime() != getHolidayPlayTime()
    }

    /**
     * 判断当前是否在夜间模式
     */
    fun isInNightMode(): Boolean {
        val canPlay: Boolean = YMDateManager.instance.betweenPlayTime()
        return !canPlay
    }

    /**
     * 获取节假日可玩时长
     */
    fun getHolidayPlayTime(): Float {
        return internalConfig.holidayPlayTime
    }

    /**
     * 获取非节假日可玩时长
     */
    fun getNormalPlayTime(): Float {
        return internalConfig.normalPlayTime
    }

    /**
     * 获取找回密码的联系方式
     */
    fun getContact(): String? {
        return internalConfig.contact
    }
}